// Copyright 2017 The Kubernetes Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package client

import (
    "log"
    "net/url"
    "strings"

    "fcs-api/src/environment"

    "github.com/emicklei/go-restful"
    "git.inspur.com/x/clustermanager-client/cluster"
    commonError "git.inspur.com/x/common/error"
    "git.inspur.com/x/common/loggerfactory"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/rest"
    restclient "k8s.io/client-go/rest"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/tools/clientcmd/api"
    Metrics "k8s.io/metrics/pkg/client/clientset/versioned"
)

// kubernetes apiserver on demand
//go:generate mockgen -destination mocks/mock_clustermanager.go -package client fcs-api/src/client ClusterClientManager
type ClusterClientManager interface {
    Client(req *restful.Request) (kubernetes.Interface, error)
    MetricsClient(req *restful.Request) (Metrics.Interface, error)
    EnvironmentClient(req *restful.Request, environmentId string) (kubernetes.Interface, error)
    EnvironmentConfig(req *restful.Request, environmentId string) (*rest.Config, error)
    InstanceClient(req *restful.Request, instance *cluster.K8sInstance) (kubernetes.Interface, error)
    ClientWithEnvironment(req *restful.Request) (environment.Interface, kubernetes.Interface, error)
    //InsecureClient(req *restful.Request) (kubernetes.Interface, error)
    //EnvironmentInsecureClient(req *restful.Request) (environment.Interface, kubernetes.Interface, error)
    Config(req *restful.Request) (*rest.Config, error)
    FCConfig() (*rest.Config, error)
    VerberClient(req *restful.Request) (*ResourceVerber, error)
    EnvironmentVerberClient(req *restful.Request, environmentId string) (*ResourceVerber, error)
}

const (
    EnvironmentPathParam  = "environment"
    ClusterPathParam      = "clusterId"
    ClusterPathPrefix     = "clusters"
    EnvironmentPathPrefix = "envs"
    JenkinsEnvironmentTag = "jenkins"
    JenkinsAdminUsername  = "admin"
    JenkinsAdminPassword  = "UhO9zzQI$a97AN#T1MdJeGLGr3PKEnx!"
)

var logger = loggerfactory.GetLogger()

// clusterClientManager implements ClientManager interface
type clusterClientManager struct {
    //cluster manger
    //get kubernetes address info
    //clusterManager cluster.Interface
    K8sCluster *cluster.K8sInstance
}

// Client returns kubernetes client that is created based on authentication information extracted
// from request. If request is nil then authentication will be skipped.
func (self *clusterClientManager) Client(req *restful.Request) (kubernetes.Interface, error) {
    cfg, err := self.Config(req)
    if err != nil {
        return nil, err
    }

    client, err := newForConfig(cfg)
    if err != nil {
        return nil, err
    }

    return client, nil
}

func (self *clusterClientManager) MetricsClient(req *restful.Request) (Metrics.Interface, error) {
    cfg, err := self.Config(req)
    if err != nil {
        return nil, err
    }

    client, err := newForMetricsConfig(cfg)
    if err != nil {
        return nil, err
    }

    return client, nil
}

// Client returns kubernetes client that is created based on authentication information extracted
// from request. If request is nil then authentication will be skipped.
func (self *clusterClientManager) EnvironmentClient(req *restful.Request, environmentId string) (kubernetes.Interface, error) {
    instance := self.K8sCluster
    cfg, err := self.InstanceConfig(req, instance)
    if err != nil {
        return nil, commonError.InternalServerError("204.001908", "集群状态异常或已删除，请确认集群信息")
    }

    client, err := newForConfig(cfg)
    if err != nil {
        return nil, commonError.InternalServerError("204.001908", "集群状态异常或已删除，请确认集群信息")
    }

    return client, nil
}

// Config creates rest Config based on authentication information extracted from request.
// Currently request header is only checked for existence of 'Authentication: BearerToken'
func (self *clusterClientManager) EnvironmentConfig(req *restful.Request, environmentId string) (*rest.Config, error) {
    instance := self.K8sCluster
    cmdConfig, err := self.clientCmdConfig(req, instance)
    if err != nil {
        _ = logger.Error("get environment %s error. %s", environmentId, err.Error())
        return nil, commonError.InternalServerError("204.001909", "集群状态异常或已删除，请确认集群信息")
    }

    cfg, err := clientCmdConfig(cmdConfig)
    if err != nil {
        _ = logger.Error("get environment %s error. %s", environmentId, err.Error())
        return nil, commonError.InternalServerError("204.001909", "集群状态异常或已删除，请确认集群信息")
    }

    self.initConfig(cfg)
    return cfg, nil
}
func clientCmdConfig(cmdConfig clientcmd.ClientConfig) (*restclient.Config, error) {
    cfg, err := cmdConfig.ClientConfig()
    if err != nil {
        _ = logger.Error("new restclient Config error from config error. %s", err.Error())
        return nil, commonError.InternalServerError("204.001907", "初始化k8s链接信息失败")
    }
    return cfg, err
}

// Client returns kubernetes client that is created based on authentication information extracted
// from request. If request is nil then authentication will be skipped.
func (self *clusterClientManager) InstanceClient(req *restful.Request, instance *cluster.K8sInstance) (kubernetes.Interface, error) {
    cfg, err := self.InstanceConfig(req, instance)
    if err != nil {
        return nil, err
    }

    client, err := newForConfig(cfg)
    if err != nil {
        return nil, err
    }

    return client, nil
}

// Config creates rest Config based on authentication information extracted from request.
// Currently request header is only checked for existence of 'Authentication: BearerToken'
func (self *clusterClientManager) InstanceConfig(req *restful.Request, instance *cluster.K8sInstance) (*rest.Config, error) {
    cmdConfig, err := self.clientCmdConfig(req, instance)
    if err != nil {
        return nil, err
    }

    cfg, err := clientCmdConfig(cmdConfig)
    if err != nil {
        return nil, err
    }

    self.initConfig(cfg)
    return cfg, nil
}

// Client returns kubernetes client that is created based on authentication information extracted
// from request. If request is nil then authentication will be skipped.
func (self *clusterClientManager) ClientWithEnvironment(req *restful.Request) (environment.Interface, kubernetes.Interface, error) {
    instance, cfg, err := self.environmentConfig(req)
    if err != nil {
        return nil, nil, err
    }

    client, err := newForConfig(cfg)
    if err != nil {
        return nil, nil, err
    }
    return environment.GetCluster(instance.Type, client), client, nil
}
func newForConfig(cfg *restclient.Config) (*kubernetes.Clientset, error) {
    client, err := kubernetes.NewForConfig(cfg)
    if err != nil {
        _ = logger.Error("new k8s client from config error. %s", err.Error())
        return nil, commonError.InternalServerError("204.001901", "初始化集群链接失败")
    }
    return client, err
}

func newForMetricsConfig(cfg *restclient.Config) (*Metrics.Clientset, error) {
    client, err := Metrics.NewForConfig(cfg)
    if err != nil {
        _ = logger.Error("new k8s client from config error. %s", err.Error())
        return nil, commonError.InternalServerError("204.001901", "初始化集群链接失败")
    }
    return client, err
}

// InsecureClient returns kubernetes client that was created without providing auth info. It uses permissions granted
// to service account used by dashboard or kubeconfig file if it was passed during dashboard init.
func (self *clusterClientManager) InsecureClient(req *restful.Request) (kubernetes.Interface, error) {
    instance := self.K8sCluster
    return self.insecureClient(instance)
}

// InsecureClient returns kubernetes client that was created without providing auth info. It uses permissions granted
// to service account used by dashboard or kubeconfig file if it was passed during dashboard init.
func (self *clusterClientManager) EnvironmentInsecureClient(req *restful.Request) (environment.Interface, kubernetes.Interface, error) {
    instance := self.K8sCluster
    client, err := self.insecureClient(instance)
    if err != nil {
        return nil, nil, err
    }
    env := environment.GetCluster(instance.Type, client)
    return env, client, nil
}

func (self *clusterClientManager) insecureClient(instance *cluster.K8sInstance) (kubernetes.Interface, error) {
    cfg, err := self.defaultConfig(instance)
    if err != nil {
        return nil, err
    }

    client, err := newForConfig(cfg)
    if err != nil {
        return nil, err
    }

    return client, nil
}

// Config creates rest Config based on authentication information extracted from request.
// Currently request header is only checked for existence of 'Authentication: BearerToken'
func (self *clusterClientManager) defaultConfig(instance *cluster.K8sInstance) (*rest.Config, error) {
    restCfg := clientConfig(instance)
    // Use auth data provided in cfg if extracted auth info is nil
    restCfg.BearerToken = instance.Authentication
    defaultAuthInfo := self.buildAuthInfoFromConfig(restCfg)
    cmdConfig := self.buildCmdConfig(&defaultAuthInfo, restCfg)
    cfg, err := clientCmdConfig(cmdConfig)
    if err != nil {
        _ = logger.Error("init client config from instance error. %s", err.Error())
        return nil, commonError.InternalServerError("204.001905", "初始化集群配置失败")
    }

    self.initConfig(cfg)
    return cfg, nil
}

// Based on rest config creates auth info structure.
func (self *clusterClientManager) buildAuthInfoFromConfig(cfg *rest.Config) api.AuthInfo {
    return api.AuthInfo{
        Token:                 cfg.BearerToken,
        ClientCertificate:     cfg.CertFile,
        ClientKey:             cfg.KeyFile,
        ClientCertificateData: cfg.CertData,
        ClientKeyData:         cfg.KeyData,
        Username:              cfg.Username,
        Password:              cfg.Password,
    }
}

// Config creates rest Config based on authentication information extracted from request.
// Currently request header is only checked for existence of 'Authentication: BearerToken'
func (self *clusterClientManager) Config(req *restful.Request) (*rest.Config, error) {
    //instance, err := self.getClusterInstance(req)
    //if err != nil {
    //	return nil, commonError.InternalServerError("201.001.901", "集群状态异常或已删除，请确认集群信息")
    //}
    /*
    	instance := &cluster.K8sInstance{
    		ApiServer:"https://10.221.128.170:6443",
    	}*/
    cmdConfig, err := self.clientCmdConfig(req, self.K8sCluster)
    if err != nil {
        _ = logger.Error("集群%v", err)
        return nil, commonError.InternalServerError("201.001.901", "集群状态异常或已删除，请确认集群信息")
    }

    cfg, err := clientCmdConfig(cmdConfig)
    if err != nil {
        _ = logger.Error("集群%v", err)
        return nil, commonError.InternalServerError("201.001.901", "集群状态异常或已删除，请确认集群信息")
    }

    self.initConfig(cfg)
    return cfg, nil
}

func (self *clusterClientManager) FCConfig() (*rest.Config, error) {
    cmdConfig, err := self.clientConfig(self.K8sCluster)
    if err != nil {
        _ = logger.Error("集群%v", err)
        return nil, commonError.InternalServerError("201.001.901", "集群状态异常或已删除，请确认集群信息")
    }

    cfg, err := clientCmdConfig(cmdConfig)
    if err != nil {
        _ = logger.Error("集群%v", err)
        return nil, commonError.InternalServerError("201.001.901", "集群状态异常或已删除，请确认集群信息")
    }

    self.initConfig(cfg)
    return cfg, nil
}

// Config creates rest Config based on authentication information extracted from request.
// Currently request header is only checked for existence of 'Authentication: BearerToken'
func (self *clusterClientManager) environmentConfig(req *restful.Request) (*cluster.K8sInstance, *rest.Config, error) {
    instance := self.K8sCluster
    cmdConfig, err := self.clientCmdConfig(req, instance)
    if err != nil {
        return nil, nil, err
    }

    cfg, err := clientCmdConfig(cmdConfig)
    if err != nil {
        return nil, nil, err
    }
    self.initConfig(cfg)
    return instance, cfg, nil
}

// clientCmdConfig creates ClientCmd Config based on authentication information extracted from request.
// Currently request header is only checked for existence of 'Authentication: BearerToken'
func (self *clusterClientManager) clientCmdConfig(req *restful.Request, instance *cluster.K8sInstance) (clientcmd.ClientConfig, error) {
    token, err := getTokenFromHeader(req)
    if err != nil {
        return nil, err
    }
    //if instance.Type == environment.ICP {
    //	client, err := self.insecureClient(instance)
    //	if err != nil {
    //		return nil, err
    //	}
    //	token = environment.GetCluster(instance.Type, client).ExtractToken(token)
    //} else {
    //	token, err = keycloak.Client.TokenExchange(token)
    //}

    var authInfo *api.AuthInfo
    if len(token) > 0 {
        authInfo = &api.AuthInfo{Token: token}
    } else {
        log.Printf("should log first")
        return nil, commonError.InvalidParameterError("204.001906", "认证信息为空，清先登录")
    }

    cfg := clientConfig(instance)

    return self.buildCmdConfig(authInfo, cfg), nil
}

// 不通过request获取config
func (self *clusterClientManager) clientConfig(instance *cluster.K8sInstance) (clientcmd.ClientConfig, error) {
    var authInfo = &api.AuthInfo{}
    cfg := clientConfig(instance)
    return self.buildCmdConfig(authInfo, cfg), nil
}

// VerberClient returns new verber client based on authentication information extracted from request
func (self *clusterClientManager) EnvironmentVerberClient(req *restful.Request, environmentId string) (*ResourceVerber, error) {
    client, err := self.EnvironmentClient(req, environmentId)
    if err != nil {
        return nil, err
    }
    verbClient := NewResourceVerber(client.CoreV1().RESTClient(),
        client.ExtensionsV1beta1().RESTClient(), client.AppsV1beta2().RESTClient(),
        client.BatchV1().RESTClient(), client.BatchV1beta1().RESTClient(), client.AutoscalingV1().RESTClient(),
        client.StorageV1().RESTClient())
    return &verbClient, nil
}

// VerberClient returns new verber client based on authentication information extracted from request
func (self *clusterClientManager) VerberClient(req *restful.Request) (*ResourceVerber, error) {
    client, err := self.Client(req)
    if err != nil {
        return nil, err
    }

    verbClient := NewResourceVerber(client.CoreV1().RESTClient(),
        client.ExtensionsV1beta1().RESTClient(), client.AppsV1beta2().RESTClient(),
        client.BatchV1().RESTClient(), client.BatchV1beta1().RESTClient(), client.AutoscalingV1().RESTClient(),
        client.StorageV1().RESTClient())
    return &verbClient, nil
}

// Initializes config with default values
func (self *clusterClientManager) initConfig(cfg *rest.Config) {
    cfg.QPS = DefaultQPS
    cfg.Burst = DefaultBurst
    cfg.ContentType = DefaultContentType
    cfg.UserAgent = DefaultUserAgent + "/" + Version
    cfg.ContentType = "application/json"
}

// Returns rest Config based on provided apiserverHost and kubeConfigPath flags. If both are
// empty then in-cluster config will be used and if it is nil the error is returned.
//func (self *clusterClientManager) getClusterInstance(req *restful.Request) (*cluster.K8sInstance, error) {
//	//env := getEnvironment(req)
//	//if env == "" {
//	//	_ = logger.Error("environment params not exist in request url. %s", req.Request.URL.Path)
//	//	return nil, commonError.InvalidParameterError("204.001901", "未指定集群，无法处理请求")
//	//}
//	//
//	//authHeader := GetAuthHeader(req)
//	//instance, err := self.clusterManager.Get(authHeader, env)
//	//
//	//if err != nil {
//	//	log.Printf("get kubernetes environment info failure, %v", err)
//	//	return nil, commonError.InvalidParameterError("204.001902", "集群状态异常或已删除，请确认集群信息")
//	//}
//
//	return &cluster.K8sInstance{ApiServer:Apiserverhost}, nil
//}

func GetAuthHeader(req *restful.Request) string {
    authHeader := req.HeaderParameter("Authorization")
    if authHeader == "" {
        authHeader = req.HeaderParameter("authorization")
    }
    return authHeader
}

func getEnvironment(req *restful.Request) string {
    env := req.PathParameter(EnvironmentPathParam)
    if env == "" {
        path := req.Request.URL.Path
        paths := strings.Split(path, "/")
        for index, subPath := range paths {
            if subPath == EnvironmentPathPrefix {
                env = paths[index+1]
            }
        }
    }
    if env == "" {
        env = req.PathParameter(ClusterPathParam)
    }
    if env == "" {
        path := req.Request.URL.Path
        paths := strings.Split(path, "/")
        for index, subPath := range paths {
            if subPath == ClusterPathPrefix {
                env = paths[index+1]
            }
        }
    }
    return env
}

// ClientConfig implements ClientConfig
func clientConfig(instance *cluster.K8sInstance) *restclient.Config {
    clientConfig := &restclient.Config{}
    clientConfig.Host = instance.ApiServer
    if u, err := url.ParseRequestURI(clientConfig.Host); err == nil && u.Opaque == "" && len(u.Path) > 1 {
        u.RawQuery = ""
        u.Fragment = ""
        clientConfig.Host = u.String()
    }
    // only try to read the auth information if we are secure
    if restclient.IsConfigTransportTLS(*clientConfig) {
        //clientConfig.BearerToken = instance.AdminToken
        clientConfig.Insecure = true
    }
    return clientConfig
}

// Based on auth info and rest config creates client cmd config.
func (self *clusterClientManager) buildCmdConfig(authInfo *api.AuthInfo, cfg *rest.Config) clientcmd.ClientConfig {
    cmdCfg := api.NewConfig()
    cmdCfg.Clusters[DefaultCmdConfigName] = &api.Cluster{
        Server:                   cfg.Host,
        CertificateAuthority:     cfg.TLSClientConfig.CAFile,
        CertificateAuthorityData: cfg.TLSClientConfig.CAData,
        InsecureSkipTLSVerify:    cfg.TLSClientConfig.Insecure,
    }
    cmdCfg.AuthInfos[DefaultCmdConfigName] = authInfo
    cmdCfg.Contexts[DefaultCmdConfigName] = &api.Context{
        Cluster:  DefaultCmdConfigName,
        AuthInfo: DefaultCmdConfigName,
    }
    cmdCfg.CurrentContext = DefaultCmdConfigName

    return clientcmd.NewDefaultClientConfig(
        *cmdCfg,
        &clientcmd.ConfigOverrides{},
    )
}

// Extracts authorization information from request header
func getTokenFromHeader(req *restful.Request) (string, error) {
    if req == nil {
        log.Print("No request provided. Skipping authorization")
        return "", commonError.InvalidParameterError("204.001903", "无request对象，无法获取认证信息")
    }

    authHeader := req.HeaderParameter("Authorization")
    if authHeader == "" {
        authHeader = req.HeaderParameter("authorization")
    }
    token := ""
    if strings.HasPrefix(authHeader, "Bearer ") {
        token = strings.TrimPrefix(authHeader, "Bearer ")
    }
    if strings.HasPrefix(authHeader, "bearer ") {
        token = strings.TrimPrefix(authHeader, "bearer ")
    }

    if len(token) > 0 {
        return token, nil
    }

    return "", commonError.InvalidParameterError("204.001904", "请求中没有认证信息")
}

// NewClientManager creates client manager based on kubeConfigPath and apiserverHost parameters.
// If both are empty then in-cluster config is used.
func NewClusterClientManager(k8sCluster *cluster.K8sInstance) ClusterClientManager {
    result := &clusterClientManager{
        K8sCluster: k8sCluster,
    }

    return result
}
